"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const defaults = require("lodash.defaults");
const ConsoleLoggerWriter_1 = require("./writer/ConsoleLoggerWriter");
class Logger {
    constructor(name, options = {}) {
        this.name = name;
        this.options = options;
        this.levels = {
            TRACE: 0,
            DEBUG: 1,
            INFO: 2,
            WARN: 3,
            ERROR: 4,
            SILENT: 5,
        };
        this.methodFactory = (methodName) => (...msg) => this.output(methodName, ...msg);
        this.level = this.getDefaultLevel();
        this.writers = [];
        if (typeof options.level !== 'undefined') {
            this.setLevel(options.level);
        }
        if (options.noDefaultWriter !== true) {
            this.addWriter(ConsoleLoggerWriter_1.ConsoleLoggerWriter);
        }
    }
    trace(...msg) {
        this.output('trace', ...msg);
    }
    debug(...msg) {
        this.output('debug', ...msg);
    }
    info(...msg) {
        this.output('info', ...msg);
    }
    warn(...msg) {
        this.output('warn', ...msg);
    }
    error(...msg) {
        this.output('error', ...msg);
    }
    /**
     * Set current logger's log level.
     * @param level
     */
    setLevel(level) {
        if (typeof level === 'number') {
            this.level = level;
            return;
        }
        level = level.toUpperCase();
        this.level = this.levels[level];
    }
    /**
     * Get current logger's log level.
     */
    getLevel() {
        return this.level;
    }
    /**
     * @deprecated Use `Logger#setLevel(level);` instead.
     * @param level
     */
    setDefaultLevel(level) {
    }
    enableAll() {
        this.level = this.levels.TRACE;
    }
    disableAll() {
        this.level = this.levels.SILENT;
    }
    /**
     * Get a logger instance by name. If `name` omitted, a global logger with name 'global' will be provided.
     * @param [name]
     * @param [options]
     */
    static getLogger(name = Logger.defaultName, options) {
        let logger = this.loggers.get(name);
        if (logger) {
            if (typeof options !== 'undefined') {
                logger.updateOptions(options);
            }
        }
        else {
            logger = new Logger(name, options);
            this.loggers.set(name, logger);
        }
        return logger;
    }
    /**
     * @deprecated Useless in Node.js.
     */
    noConflict() {
    }
    /**
     * Get a logger instance by name.
     * @param name
     * @param [options]
     */
    getLogger(name, options) {
        return Logger.getLogger(name, options);
    }
    /**
     * @deprecated Alias for `Logger#debug(...msg)`.
     * @param msg
     */
    log(...msg) {
        this.output('debug', ...msg);
    }
    /**
     * Add new write to current logger. Used to change the way the logger logs.
     * @param Writer
     * @param [options]
     */
    addWriter(Writer, options) {
        options = defaults(options, this.options, {
            traceFunction: this.trace,
        });
        const writer = new Writer(this.name, options);
        this.writers.push(writer);
        return this;
    }
    updateOptions(options) {
        if (options.level) {
            this.setLevel(options.level);
        }
        for (const writer of this.writers) {
            writer.updateOptions(options);
        }
        return this;
    }
    /**
     * Get default level for logger from environment. Default to 'info'.
     * @protected
     */
    getDefaultLevel() {
        let flags = process.env.DEBUG;
        if (typeof flags === 'string') {
            if (flags === '1' || flags === 'true') {
                flags = Logger.defaultName;
            }
            const tester = new RegExp(`^${flags
                .replace(/\*+/g, '.*?')
                .replace(/,/g, '|')}$`);
            if (tester.test(this.name)) {
                return this.levels.TRACE;
            }
        }
        return this.levels.INFO;
    }
    /**
     * Internal log method. Call each writer when logging.
     * @protected
     * @param methodName
     * @param msg
     */
    output(methodName, ...msg) {
        if (this.levels[methodName.toUpperCase()] < this.level) {
            return;
        }
        const timestamp = new Date();
        for (const writer of this.writers) {
            writer.write(methodName, timestamp, ...msg);
        }
    }
}
exports.Logger = Logger;
Logger.defaultName = 'global';
Logger.loggers = new Map();
